// 对libuv 不过渡封装 完成相关接口的适配即可
#include "LibuvServlet.h"
#include "ILogger.h"
#include "string.h"
/////////////////////////////////////////////////
//////////////LibuvClientServlet/////////////////
/////////////////////////////////////////////////

typedef struct
{
    uv_write_t req;
    uv_buf_t buf;
} write_req_t;

static void after_read(uv_stream_t *handle,
                       ssize_t nread,
                       const uv_buf_t *buf);
static void after_read_client(uv_stream_t *handle,
                       ssize_t nread,
                       const uv_buf_t *buf);

static void do_alloc(uv_handle_t *handle,
                     size_t suggested_size,
                     uv_buf_t *buf);



static void on_close(uv_handle_t *peer);
static void on_close_client(uv_handle_t *peer);

static void after_write(uv_write_t *req, int status);

static void connect_cb(uv_connect_t *req, int status);
static void on_connection(uv_stream_t *server, int status);


class LibuvSession : public ISession
{
public:
    void reply(const void *data, int len)
    {
        write_req_t *wr;
        wr = (write_req_t *)malloc(sizeof *wr);
        if (!wr)
        {
            LogE("malloc failed");
            return;
        }
        char *buf = (char *)malloc(len);
        memcpy(buf, data, len);
        wr->buf = uv_buf_init(buf, len);

        if (uv_write(&wr->req, (uv_stream_t *)handler, &wr->buf, 1, after_write))
        {
            LogE("uv_write failed");
        }
    }
    void close()
    {
        uv_close(handler, on_close);
    }
    LibuvSession(uv_handle_t *handler, IServlet *servlet) : handler(handler), servlet(servlet)
    {
    }
    uv_handle_t *handler;
    IServlet *servlet;
};

void LibuvClientServlet::on(SERVLET_STATUS type, std::function<int(ISession *session, void *, int)> handler)
{
}
void LibuvClientServlet::request(const void *data, int len)
{
    if (clientSession)
        clientSession->reply(data,len);
}

void LibuvClientServlet::start()
{
    struct sockaddr_in addr;

    int r = -1;

    r = uv_ip4_addr(ip.c_str(), port, &addr);
    if (r)
    {
        LogE("uv_ip4_addr error!");
    }
    loop = uv_default_loop();

    r = uv_tcp_init(loop, &tcp_client);
    if (r)
    {
        LogE("uv_tcp_init error!");
    }

    connect_req.data = this;
    r = uv_tcp_connect(&connect_req,
                       &tcp_client,
                       (const struct sockaddr *)&addr,
                       connect_cb);
    if (r)
    {
        LogE("uv_tcp_connect error!");
    }
}
void LibuvClientServlet::wait()
{
}
void LibuvClientServlet::stop()
{
}
void LibuvClientServlet::onLoad(INode *node)
{
    INode *child = node->childNode("connection");
    if (child)
    {
        ip = child->getStr("ip");
        port = child->get<int>("port");
    }
    child = node->childNode("withloop");
    if (child)
    {
        withLoop = child->get<int>("yes");
    }
    start();
}
void LibuvClientServlet::onUnload()
{
    if (withLoop)
        uv_run(loop, UV_RUN_DEFAULT);
}
void LibuvClientServlet::onMessage(ISession *session, void *data, int size)
{
}
void LibuvClientServlet::onState(SERVLET_STATUS status, ISession *session, int code)
{
}
LibuvClientServlet::~LibuvClientServlet()
{
}

/////////////////////////////////////////////////
//////////////LibuvClientServlet/////////////////
/////////////////////////////////////////////////

/////////////////////////////////////////////////
//////////////LibUvServerServlet/////////////////
/////////////////////////////////////////////////

void LibuvServerServlet::start()
{
    loop = uv_default_loop();
    struct sockaddr_in addr;
    int r = -1;
    uv_handle_t *server;

    r = uv_ip4_addr("127.0.0.1", port, &addr);
    if (r != 0)
    {
        LogE("uv_ip4_addr error:%s", uv_err_name(r));
    }
    server = (uv_handle_t *)&tcpServer;

    r = uv_tcp_init(loop, &tcpServer);
    if (r)
    {
        /* TODO: Error codes */

        LogE("Socket creation error:%s", uv_err_name(r));
        return;
    }

    r = uv_tcp_bind(&tcpServer, (const struct sockaddr *)&addr, 0);
    if (r)
    {
        /* TODO: Error codes */
        LogE("Bind error:%s", uv_err_name(r));
        return;
    }
    tcpServer.data = this;
    r = uv_listen((uv_stream_t *)&tcpServer, SOMAXCONN, on_connection);
    if (r)
    {
        /* TODO: Error codes */
        LogE("Listen error:%s", uv_err_name(r));
        return;
    }

    return;
}
void LibuvServerServlet::wait()
{
}
void LibuvServerServlet::stop()
{
}
void LibuvServerServlet::onLoad(INode *node)
{
    INode *child = node->childNode("connection");
    if (child)
    {
        port = child->get<int>("port");
        ip = child->getStr("ip");
    }
    child = node->childNode("withloop");
    if (child)
    {
        withLoop = child->get<int>("yes");
    }
    start();
}
void LibuvServerServlet::onUnload()
{
    if (withLoop)
        uv_run(loop, UV_RUN_DEFAULT);
}
void LibuvServerServlet::onMessage(ISession *session, void *data, int size)
{
}
void LibuvServerServlet::onState(SERVLET_STATUS status, ISession *session, int code)
{
}
LibuvServerServlet::~LibuvServerServlet()
{
}

void LibuvServerServlet::on(SERVLET_STATUS type, std::function<int(ISession *session, void *, int)> handler)
{
}
void LibuvServerServlet::request(const void *data, int len)
{
}

/////////////////////////////////////////////////
//////////////LibUvServerServlet/////////////////
/////////////////////////////////////////////////

//////////////////////////////////////////////////
/////////////////////LIBUV////////////////////////
//////////////////////////////////////////////////

static void on_close(uv_handle_t *peer)
{
    delete (LibuvSession *)peer->data;
    LogI("delete session:%p", peer->data);
    free(peer);
}

static void on_close_client(uv_handle_t *peer)
{
    delete (LibuvSession *)peer->data;
    LogI("delete session:%p", peer->data);
}

static void connect_cb(uv_connect_t *req, int status)
{

    if (status != 0)
    {
        LogE("connect_cb error!");
    }

    /* Close the client. */
    uv_stream_t *stream = (uv_stream_t *)req->handle;
    LibuvClientServlet *servlet = (LibuvClientServlet *)req->data;
    LibuvSession *session = new LibuvSession((uv_handle_t *)stream, servlet);
    servlet->clientSession = session;
    stream->data = session;
    LogI("new session:%p", session);
    uv_read_start(stream, do_alloc, after_read_client);
}

static void after_read_client(uv_stream_t *handle,
                              ssize_t nread,
                              const uv_buf_t *buf)
{

    LibuvSession *session = (LibuvSession *)handle->data;
    if (nread < 0)
    {
        /* Error or EOF */
        if (nread == UV_EOF)
        {
            LogE("Read error %s", uv_err_name(nread));
        }
        else
        {
            LogI("client disconnected");
        }

        free(buf->base);

        uv_close((uv_handle_t *)handle, on_close_client);

        return;
    }

    if (nread == 0)
    {
        /* Everything OK, but nothing read. */
        free(buf->base);
        return;
    }

    if (session && session->servlet)
    {
        session->servlet->onMessage(session, buf->base, nread); // deal with the message
    }
}

static void do_alloc(uv_handle_t *handle,
                     size_t suggested_size,
                     uv_buf_t *buf)
{
    buf->base = (char *)malloc(suggested_size);
    buf->len = suggested_size;
}

static void on_connection(uv_stream_t *server, int status)
{
    uv_stream_t *stream = NULL;
    int r = -1;

    if (status != 0)
    {
        LogE("Connect error %s", uv_err_name(status));
    }

    stream = (uv_stream_t *)malloc(sizeof(uv_tcp_t));
    LibuvServerServlet *servlet = (LibuvServerServlet *)server->data;
    r = uv_tcp_init(servlet->loop, (uv_tcp_t *)stream);

    /* associate server with stream */
    LibuvSession *session = new LibuvSession((uv_handle_t *)stream, servlet);
    stream->data = session;
    LogI("new session:%p", session);
    r = uv_accept(server, stream);
    if (r != 0)
    {
        LogE("uv_accept error %s", uv_err_name(r));
    }

    r = uv_read_start(stream, do_alloc, after_read);
    if (r != 0)
    {
        LogE("uv_read_start error %s", uv_err_name(r));
    }
}

static void after_write(uv_write_t *req, int status)
{
    write_req_t *wr;

    /* Free the read/write buffer and the request */
    wr = (write_req_t *)req;
    free(wr->buf.base);
    free(wr);

    if (status == 0)
        return;

    LogE("uv_write error: %s - %s\n", uv_err_name(status), uv_strerror(status));
}

static void after_shutdown(uv_shutdown_t *req, int status)
{
    LogI("status:%d", status);
    uv_close((uv_handle_t *)req->handle, on_close);
    free(req);
}

static void after_read(uv_stream_t *handle,
                       ssize_t nread,
                       const uv_buf_t *buf)
{
    uv_shutdown_t *sreq;

    if (nread < 0)
    {
        /* Error or EOF */
        if (nread == UV_EOF)
        {
            LogE("Read error %s", uv_err_name(nread));
        }
        else
        {
            LogI("client disconnected");
        }

        free(buf->base);
        sreq = (uv_shutdown_t *)malloc(sizeof *sreq);
        if (uv_is_writable(handle))
        {
            uv_shutdown(sreq, handle, after_shutdown);
        }
        else
        {
            uv_close((uv_handle_t *)handle, on_close);
        }
        return;
    }

    if (nread == 0)
    {
        /* Everything OK, but nothing read. */
        free(buf->base);
        return;
    }
    LibuvSession *session = (LibuvSession *)handle->data;
    if (session && session->servlet)
    {
        session->servlet->onMessage(session, buf->base, nread); // deal with the message
    }
}

//////////////////////////////////////////////////
/////////////////////LIBUV////////////////////////
//////////////////////////////////////////////////